import { useState, useEffect, useMemo, useRef } from "react";
import { useNavigate, Link, useLocation } from "react-router";
import { toBytes } from "fast-base64";
import { decompress } from "lzma-js-simple-v2";
import { alert } from "./mdui";
import { toast, copyText, addTimeParameter, hints } from "./utilities";
import Checker from "./Checker";
import classes from "./CodePage.module.css";
/* prism */
import { PrismAsyncLight as SyntaxHighlighter } from "react-syntax-highlighter";
import js from "react-syntax-highlighter/dist/esm/languages/prism/js-extras";
import css from "react-syntax-highlighter/dist/esm/languages/prism/css-extras";
import html from "react-syntax-highlighter/dist/esm/languages/prism/markup";
import lightStyle from "react-syntax-highlighter/dist/esm/styles/prism/material-light";
import darkStyle from "react-syntax-highlighter/dist/esm/styles/prism/material-dark";

const viaLogo =
  '<img class="smaller" src="file:///android_asset/logo.svg" />\n';

SyntaxHighlighter.registerLanguage("javascript", js);
SyntaxHighlighter.registerLanguage("css", css);
SyntaxHighlighter.registerLanguage("html", html);

type yujianBackSettings = {
  key: string;
  storage: number;
  value: string | number | boolean;
}[];

interface codeDataType {
  html: string;
  css: string;
  hint: keyof typeof hints;
}

const CodeArea = (properties: {
  title: string;
  code: string;
  lang: "html" | "css";
  showLine: boolean;
  hint: keyof typeof hints;
  dark: boolean;
  logo?: boolean;
}) => {
  const [expand, setexpand] = useState(false);
  const code = useMemo(
    () => (properties.logo ? viaLogo : "") + properties.code,
    [properties.logo, properties.code],
  );
  const hint = useMemo(() => {
    return hints[properties.hint][properties.lang];
  }, [properties.lang, properties.hint]);
  const codeBoxReference = useRef<HTMLElement>(null);

  return (
    <fieldset className={"ui " + classes.box}>
      <legend>
        <strong>{properties.title}</strong>
      </legend>
      <section
        ref={codeBoxReference}
        className={expand ? classes.expand : ""}
        onClick={() => {
          const value = expand;
          setexpand(!value);
        }}
      >
        <SyntaxHighlighter
          language={properties.lang}
          style={properties.dark ? darkStyle : lightStyle}
          showLineNumbers={properties.showLine}
          lineNumberStyle={{
            paddingRight: 8,
          }}
          customStyle={{
            padding: ".5em 4px",
          }}
        >
          {code}
        </SyntaxHighlighter>
      </section>
      <mdui-segmented-button-group full-width>
        <mdui-segmented-button
          icon="arrow_circle_up"
          onClick={() => {
            codeBoxReference.current?.scrollTo({
              top: 0,
              left: 0,
              behavior: "smooth",
            });
          }}
        >
          到顶
        </mdui-segmented-button>
        <mdui-segmented-button
          icon="arrow_circle_down"
          onClick={() => {
            codeBoxReference.current?.scrollTo({
              top: codeBoxReference.current.scrollHeight,
              left: 0,
              behavior: "smooth",
            });
          }}
        >
          到底
        </mdui-segmented-button>
        <mdui-segmented-button
          icon="content_copy"
          onClick={() => {
            void copyText(code, () => {
              void alert({
                description: hint,
                confirmText: "好",
                closeOnOverlayClick: true,
              });
            });
          }}
        >
          复制
        </mdui-segmented-button>
      </mdui-segmented-button-group>
    </fieldset>
  );
};

const decoder = new TextDecoder("utf8");

function decode(data: string) {
  const parsed = data
    .replace(/\\n/g, "")
    .replace(/\\\//g, "/")
    .replace(/\n/g, "");
  return toBytes(parsed);
}

async function parseViaConfig(text: string): Promise<codeDataType> {
  const result: codeDataType = {
    html: "",
    css: "",
    hint: "via",
  };
  const configGroups = text.split("\n");
  for (const config of configGroups) {
    if (config.startsWith('{"settings":{')) {
      const configObject = JSON.parse(config) as Record<
        string,
        Record<string, string>
      >;
      if (configObject.settings.csstheme) {
        const ua = await decode(configObject.settings.csstheme);
        result.css = decoder.decode(ua);
      }
      if (configObject.settings.taghome) {
        const ua = await decode(configObject.settings.taghome);
        result.html = decoder.decode(ua);
      }
    }
  }
  return result;
}

function parseYujianConfig(text: string): Promise<codeDataType> {
  const result: codeDataType = {
    html: "",
    css: "",
    hint: "yujian",
  };
  const configs = JSON.parse(text) as yujianBackSettings;
  for (const config of configs) {
    if (config.key === "CUSTCOMHTMLCSS" && config.value) {
      result.css = config.value as string;
    }
    if (config.key === "CUSTCOMHTML" && config.value) {
      result.html = config.value as string;
    }
  }
  return Promise.resolve(result);
}

const CodePage = (properties: { dark: boolean }) => {
  const nav = useNavigate();
  const [htmlCode, sethtmlCode] = useState("");
  const [cssCode, setcssCode] = useState("");
  const [addLogo, setaddLogo] = useState(false);
  const [showLine, setshowLine] = useState(false);
  const [hint, setHint] = useState<keyof typeof hints>("via");
  const loc = useLocation();

  useEffect(() => {
    if (loc.search.startsWith("?url=")) {
      const url = loc.search.replace("?url=", "");
      if (/^https?:\/\//.test(url)) {
        fetch(addTimeParameter(url))
          .then((resp) => {
            if (resp.ok) {
              return resp.text();
            } else {
              throw new Error(String(resp.status));
            }
          })
          .then((text) => {
            const yujianFormat = url.endsWith(".back");
            const dataPromise = yujianFormat
              ? parseYujianConfig(text)
              : parseViaConfig(text);
            dataPromise
              .then((data) => {
                setHint(data.hint);
                if (data.html.length > 0) {
                  sethtmlCode(data.html);
                }
                if (data.css.length > 0) {
                  setcssCode(data.css);
                }
              })
              .catch((error: unknown) => {
                toast(`解码失败：${String(error)}`);
                console.error(error);
              });
          })
          .catch((error: unknown) => {
            toast(`获取失败：${String(error)}`, () => {
              void nav(-1);
            });
            console.error(error);
          });
      } else {
        toast("地址解析失败", () => {
          void nav(-1);
        });
      }
    } else if (loc.search.startsWith("?code=")) {
      try {
        toBytes(loc.search.replace("?code=", ""))
          .then((result) => {
            decompress(result, (data) => {
              const codeData = JSON.parse(data as string) as {
                html: string;
                css: string;
                hint?: keyof typeof hints;
              };
              if (codeData.html.length + codeData.css.length === 0) {
                toast("空代码", () => {
                  void nav(-1);
                });
              }
              sethtmlCode(codeData.html);
              setcssCode(codeData.css);
              if (codeData.hint) {
                setHint(codeData.hint);
              }
            });
          })
          .catch((error) => {
            toast("解码时出现错误: " + String(error), () => {
              void nav(-1);
            });
          });
      } catch (error) {
        toast(`处理失败：${String(error)}`, () => {
          void nav(-1);
        });
        console.error(error);
      }
    } else {
      toast("数据解析失败", () => {
        void nav("../");
      });
    }
  }, [nav, loc.search]);

  return (
    <main>
      {htmlCode ? (
        <CodeArea
          title="HTML 代码"
          code={htmlCode}
          lang="html"
          logo={addLogo}
          showLine={showLine}
          hint={hint}
          dark={properties.dark}
        />
      ) : (
        <></>
      )}
      {cssCode ? (
        <CodeArea
          title="自定义 CSS"
          code={cssCode}
          lang="css"
          showLine={showLine}
          hint={hint}
          dark={properties.dark}
        />
      ) : (
        <></>
      )}
      <>
        {htmlCode && hint === "via" ? (
          <Checker
            checked={addLogo}
            call={() => {
              const value = addLogo;
              setaddLogo(!value);
            }}
          >
            补加 Via 默认 Logo
          </Checker>
        ) : (
          <></>
        )}
        {htmlCode || cssCode ? (
          <Checker
            checked={showLine}
            call={() => {
              const value = showLine;
              setshowLine(!value);
            }}
          >
            显示行数
          </Checker>
        ) : (
          <div
            className="center"
            id="loading"
            style={{
              margin: "30vh auto",
            }}
          >
            正在加载...
          </div>
        )}
      </>
      <Link to="/licenses" className="no-highlight">
        <mdui-button icon="info--outlined" variant="tonal">
          版本与依赖组件信息
        </mdui-button>
      </Link>
      <Link
        to="/"
        className="no-highlight"
        state={{
          html: htmlCode,
          css: cssCode,
          hint,
        }}
      >
        <mdui-button
          style={{
            marginLeft: 10,
          }}
          icon="edit"
          variant="tonal"
        >
          编辑与处理
        </mdui-button>
      </Link>
    </main>
  );
};

export default CodePage;
